package com.gz.spring.annotation;


import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  GzSpringApplicationContext 类的作用类似于Spring原生ioc容器
 */
public class GzApplicationContext {
    private Class configClass;
    //ioc存放的就是通过反射创建的对象(基于注解的方式)
    private ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public ConcurrentHashMap<String, Object> getIoc() {
        return ioc;
    }

    public void setIoc(ConcurrentHashMap<String, Object> ioc) {
        this.ioc = ioc;
    }

    public GzApplicationContext(Class configClass) {
        this.configClass = configClass;
        //获得配置文件(这里是GzSpringConfig的注解)
        ComponentScan componentScan= (ComponentScan)this.configClass.
                getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("要扫描的包="+path); //com.gz.spring.component

        //得到类加载器
        ClassLoader classLoader = GzApplicationContext.class.getClassLoader();
        //通过类加载器获取到要扫描的包的资源 url
        //注意这里不直接用path是因为path是com.gz.spring.component,
        //而getResource需要的是     com/gz/spring/component
        path= path.replace('.', '/');
        URL resource = classLoader.getResource(path);
        //resource=/E:/Java/java_code/spring/out/production/spring/com/gz/spring/component
        System.out.println("resource="+resource);

        //将要加载的资源(.class)路径下的文件进行遍历(io)
        File file = new File(resource.getFile());
        if(file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println(f.getAbsolutePath());
                //E:\Java\java_code\spring\out\production\spring\com\gz\spring\component\MyComponent.class
                String absolutePath = f.getAbsolutePath();

                //这里过滤一下，只要.class结尾的
                if(absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    //substring是前闭后开[)
                    //得到类名MyComponent
                    String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1
                            , absolutePath.indexOf(".class"));
                    System.out.println(className);
                    //2.得到com.gz.spring.component.MyComponent
                    String classFullName=path.replace("/",".")+"."+className;
                    System.out.println(classFullName);

                    //3.判断该类是不是需要注入容器，因为有的类上面没有写@Controller、@Repository等注解
                    //这里用Class.forName(classFullName)也行
                    //两者区别:
                    //(1)Class.forName会调用静态方法
                    //(2)classLoader.loadClass不会，可以理解为轻量级的
                    try {
                        //Class<?> aClass = Class.forName(classFullName);
                        Class<?> aClass = classLoader.loadClass(classFullName);
                        if(aClass.isAnnotationPresent(Component.class) ||
                                aClass.isAnnotationPresent(Controller.class) ||
                                aClass.isAnnotationPresent(Repository.class) ||
                                aClass.isAnnotationPresent(Service.class) ) {

                            //如果注解指定了id(如@Controller(value=""gz2)),
                            //那么把classname改成gz2,即分配id(自己制定的key)
                            if(aClass.isAnnotationPresent(Component.class)){
                                String id=aClass.getAnnotation(Component.class).value();
                                //如果设置了value
                                //如果没设置，默认首字母小写的
                                if(!"".equals(id)){
                                    className=id;//替换
                                }
                            } else if (aClass.isAnnotationPresent(Controller.class)) {
                                String id=aClass.getAnnotation(Controller.class).value();
                                if(!"".equals(id)){
                                    className=id;//替换
                                }
                            } else if (aClass.isAnnotationPresent(Repository.class)) {
                                String id=aClass.getAnnotation(Repository.class).value();
                                if(!"".equals(id)){
                                    className=id;//替换
                                }
                            } else if (aClass.isAnnotationPresent(Service.class)) {
                                String id=aClass.getAnnotation(Service.class).value();
                                if(!"".equals(id)){
                                    className=id;//替换
                                }
                            }

                            //这时就可以获得反射对象
                            //这时可以用Class.forName方法了
                            Class<?> clazz = Class.forName(classFullName);
                            Object o = clazz.newInstance();

                            //用StringUtils将className的首字母变成小写
                            className= StringUtils.uncapitalize(className);
                            //放入到容器中
                            ioc.put(className, o);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}


